package org.etnaframework.module.base.utils;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TSFBuilder;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.introspect.AnnotationMap;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 序列号/反序列化JSON的工具，基于jackson再包装
 *
 * @author jasyaf
 * @since 2023-09-01
 */
public class JacksonUtils {

    private static final Logger log = LoggerFactory.getLogger(JacksonUtils.class);

    public static final ObjectMapper DEFAULT_JACKSON_MAPPER;

    public static final ObjectMapper PRETTY_JACKSON_MAPPER;

    static {
        TSFBuilder<?, ?> jfb = JsonFactory.builder();
        jfb.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
        jfb.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
        jfb.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true);
        jfb.configure(JsonReadFeature.ALLOW_YAML_COMMENTS, true);
        jfb.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES, true);
        jfb.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS, true);
        jfb.configure(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        jfb.configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true);
        jfb.configure(JsonReadFeature.ALLOW_MISSING_VALUES, true); // 为了能解析[1,2,3,]这样在3后面加,的非标准json，必须打开这个选项，但注意这会让解析的结果List最后一个是null
        JsonFactory jf = jfb.build();

        DateFormat defaultDF = new SimpleDateFormat(DatetimeUtils.DF_yyyy_MM_dd_HHmmss);

        // LocalDateTime & LocalDate
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_yyyy_MM_dd_HHmmss)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_yyyy_MM_dd)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_HH_mm_ss)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_yyyy_MM_dd_HHmmss)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_yyyy_MM_dd)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatetimeUtils.DF_HH_mm_ss)));

        // 针对数值类型的特别处理：
        // 1、long为防止输出到前端时因精度丢失导致JS无法处理，转换为字符串再输出
        // 2、反序列化时，如果数值有问题（例如空，或其他无法解析的字符串），目标对象用Integer/Long/Float/Double来接会获取到null，用int/long/float/double来接就报错
        SimpleModule numberModule = new SimpleModule();
        numberModule.addSerializer(Long.class, new LongSerializer());
        numberModule.addSerializer(long.class, new LongSerializer());
        numberModule.addDeserializer(Long.class, new LongDeserializer());
        numberModule.addDeserializer(Integer.class, new IntegerDeserializer());
        numberModule.addDeserializer(Float.class, new FloatDeserializer());
        numberModule.addDeserializer(Double.class, new DoubleDeserializer());

        DEFAULT_JACKSON_MAPPER = new ObjectMapper(jf);
        DEFAULT_JACKSON_MAPPER.setDateFormat(defaultDF);
        DEFAULT_JACKSON_MAPPER.setSerializationInclusion(Include.NON_NULL);// 如果json的key对应的value为null，序列化出去的json在iOS客户端解析会崩溃，故这种情况下让结果集中不包含这个key
        DEFAULT_JACKSON_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        DEFAULT_JACKSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略json字符串中存在而Java对象实际没有的属性
        DEFAULT_JACKSON_MAPPER.registerModule(javaTimeModule);
        DEFAULT_JACKSON_MAPPER.registerModule(numberModule);

        PRETTY_JACKSON_MAPPER = new ObjectMapper(jf);
        PRETTY_JACKSON_MAPPER.setDateFormat(defaultDF);
        PRETTY_JACKSON_MAPPER.setSerializationInclusion(Include.NON_NULL); // 如果json的key对应的value为null，序列化出去的json在iOS客户端解析会崩溃，故这种情况下让结果集中不包含这个key
        PRETTY_JACKSON_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        PRETTY_JACKSON_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略json字符串中存在而Java对象实际没有的属性
        PRETTY_JACKSON_MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true); // 格式化输出
        PRETTY_JACKSON_MAPPER.registerModule(javaTimeModule);
        PRETTY_JACKSON_MAPPER.registerModule(numberModule);
    }

    private static boolean _isWrap(CharSequence str, char prefixChar, char suffixChar) {
        if (null == str) {
            return false;
        } else {
            return str.charAt(0) == prefixChar && str.charAt(str.length() - 1) == suffixChar;
        }
    }

    /**
     * 是否为JSON字符串，首尾都为大括号或中括号判定为JSON字符串
     */
    public static boolean isJson(String str) {
        return isJsonObj(str) || isJsonArray(str);
    }

    /**
     * 快速判断是否为JSON对象
     */
    public static boolean isJsonObj(String str) {
        if (null == str || str.isEmpty()) {
            return false;
        }
        return _isWrap(str.trim(), '{', '}');
    }

    /**
     * 快速判断是否为JSON数组
     */
    public static boolean isJsonArray(String str) {
        if (null == str || str.isEmpty()) {
            return false;
        }
        return _isWrap(str.trim(), '[', ']');
    }

    /**
     * 将传入的对象生成JSON字符串
     *
     * @return 如果传入null对象或转换失败，返回null
     */
    public static String toJson(Object obj) {
        try {
            return DEFAULT_JACKSON_MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("toJson failed, type={}", null == obj ? null : obj.getClass(), e);
            }
        }
        return null;
    }

    /**
     * 将传入的对象生成格式化后的JSON字符串
     *
     * @return 如果传入null对象或转换失败，返回null
     */
    public static String toJsonPretty(Object obj) {
        try {
            return PRETTY_JACKSON_MAPPER.writeValueAsString(obj);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("toJson failed, type={}", null == obj ? null : obj.getClass(), e);
            }
        }
        return null;
    }

    /**
     * 将传入的对象生成JSON字符串bytes
     *
     * @return 如果传入null对象或转换失败，返回null
     */
    public static byte[] toBytes(Object obj) {
        try {
            return DEFAULT_JACKSON_MAPPER.writeValueAsBytes(obj);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("toJson failed, type={}", null == obj ? null : obj.getClass(), e);
            }
        }
        return null;
    }

    /**
     * 将json字符串转换为{@link DbMap}（内部是Map<String,Object>）
     *
     * @return 如果传入空字符串或转换失败，返回空{@link DbMap}，注意不会返回null
     */
    @NonNull
    public static DbMap toMap(String jsonString) {
        if (null == jsonString || jsonString.isEmpty()) {
            return new DbMap();
        }
        try {
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, DbMap.class);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("toMap Failed, jsonString={}", jsonString, e);
            }
        }
        return new DbMap();
    }

    /**
     * 将传入的对象生成{@link DbMap}，如果传入null对象或转换失败，返回空{@link DbMap}，注意不会返回null
     */
    public static DbMap toMap(Object obj) {
        return toMap(toJson(obj));
    }

    /**
     * 异常处理，针对没有无参构造方法的情况单独给出提醒
     */
    private static <T> void _handleJacksonException(Throwable ex, Class<T> requiredClass) {
        if (ex.getMessage().toLowerCase().contains("construct instance")) {
            throw new IllegalArgumentException("Please add a PUBLIC constructor without parameters for class " + requiredClass.getName() + ", otherwise the object instance cannot be created", ex);
        }
    }

    /**
     * 异常处理，针对没有无参构造方法的情况单独给出提醒
     */
    private static <T> void _handleJacksonException(Throwable ex, TypeReference<T> requiredType) {
        if (ex.getMessage().toLowerCase().contains("construct instance")) {
            throw new IllegalArgumentException("Please add a PUBLIC constructor without parameters for class " + requiredType.getType().getTypeName() + ", otherwise the object instance cannot be created", ex);
        }
    }

    /**
     * 将JSON字符串转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(byte[] jsonBytes, Class<T> requiredClass) {
        if (null == jsonBytes || jsonBytes.length == 0) {
            return null;
        }
        try {
            return DEFAULT_JACKSON_MAPPER.readValue(jsonBytes, requiredClass);
        } catch (Exception e) {
            _handleJacksonException(e, requiredClass);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredClass={}, jsonString={}", requiredClass, new String(jsonBytes, StandardCharsets.UTF_8), e);
            }
        }
        return null;
    }

    /**
     * 将JSON字符串转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(byte[] jsonBytes, TypeReference<T> requiredType) {
        if (null == jsonBytes || jsonBytes.length == 0) {
            return null;
        }
        try {
            return DEFAULT_JACKSON_MAPPER.readValue(jsonBytes, requiredType);
        } catch (Exception e) {
            _handleJacksonException(e, requiredType);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredType={}, jsonString={}", requiredType, new String(jsonBytes, StandardCharsets.UTF_8), e);
            }
        }
        return null;
    }

    /**
     * 将JSON字符串转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(String jsonString, Class<T> requiredClass) {
        if (null == jsonString || jsonString.isEmpty()) {
            return null;
        }
        try {
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, requiredClass);
        } catch (Exception e) {
            _handleJacksonException(e, requiredClass);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredClass={}, jsonString={}", requiredClass, jsonString, e);
            }
        }
        return null;
    }

    /**
     * 将JSON字符串转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(String jsonString, TypeReference<T> requiredType) {
        if (null == jsonString || jsonString.isEmpty()) {
            return null;
        }
        try {
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, requiredType);
        } catch (Exception e) {
            _handleJacksonException(e, requiredType);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredType={}, jsonString={}", requiredType, jsonString, e);
            }
        }
        return null;
    }

    /**
     * 将Map转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(Map<?, ?> map, Class<T> requiredClass) {
        if (null == map || map.isEmpty()) {
            return null;
        }
        String jsonString = null;
        try {
            jsonString = toJson(map);
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, requiredClass);
        } catch (Exception e) {
            _handleJacksonException(e, requiredClass);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredClass={}, jsonString={}", requiredClass, jsonString, e);
            }
        }
        return null;
    }

    /**
     * 将Map转化为指定的对象
     *
     * @return 如果传入空值或转换失败，返回null
     */
    public static <T> T toBean(Map<?, ?> map, TypeReference<T> requiredType) {
        if (null == map || map.isEmpty()) {
            return null;
        }
        String jsonString = null;
        try {
            jsonString = toJson(map);
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, requiredType);
        } catch (Exception e) {
            _handleJacksonException(e, requiredType);
            if (log.isDebugEnabled()) {
                log.debug("toBean Failed, requiredType={}, jsonString={}", requiredType, jsonString, e);
            }
        }
        return null;
    }

    /**
     * 将json字符串转换为List
     *
     * @return 如果传入空值或转换失败，返回空列表，注意不会返回null
     */
    public static <T> List<T> toList(String jsonString, Class<T> requiredClass) {
        if (null == jsonString || jsonString.isEmpty()) {
            return new ArrayList<>();
        }
        try {
            JavaType jt = DEFAULT_JACKSON_MAPPER.getTypeFactory().constructCollectionType(List.class, requiredClass);
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, jt);
        } catch (Exception e) {
            _handleJacksonException(e, requiredClass);
            if (log.isDebugEnabled()) {
                log.debug("toList Failed, requiredClass={}, jsonString={}", requiredClass, jsonString, e);
            }
        }
        return new ArrayList<>();
    }

    /**
     * 将json字符串转换为List
     *
     * @return 如果传入空值或转换失败，返回空列表，注意不会返回null
     */
    public static List<DbMap> toList(String jsonString) {
        if (null == jsonString || jsonString.isEmpty()) {
            return new ArrayList<>();
        }
        try {
            JavaType jt = DEFAULT_JACKSON_MAPPER.getTypeFactory().constructCollectionType(List.class, DbMap.class);
            return DEFAULT_JACKSON_MAPPER.readValue(jsonString, jt);
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug("toList Failed, requiredClass={}, jsonString={}", DbMap.class, jsonString, e);
            }
        }
        return new ArrayList<>();
    }

    public static class DoubleDeserializer extends StdDeserializer<Double> {

        private static final long serialVersionUID = 1L;

        public DoubleDeserializer() {
            super(Long.class);
        }

        @Override
        public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            String text = jp.getText();
            if (null == text || text.isEmpty()) {
                return null;
            }
            try {
                return Double.valueOf(text);
            } catch (NumberFormatException ignore) {
                return null;
            }
        }
    }

    public static class FloatDeserializer extends StdDeserializer<Float> {

        private static final long serialVersionUID = 1L;

        public FloatDeserializer() {
            super(Long.class);
        }

        @Override
        public Float deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            String text = jp.getText();
            if (null == text || text.isEmpty()) {
                return null;
            }
            try {
                return Float.valueOf(text);
            } catch (NumberFormatException ignore) {
                return null;
            }
        }
    }

    public static class IntegerDeserializer extends StdDeserializer<Integer> {

        private static final long serialVersionUID = 1L;

        public IntegerDeserializer() {
            super(Long.class);
        }

        @Override
        public Integer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            String text = jp.getText();
            if (null == text || text.isEmpty()) {
                return null;
            }
            try {
                return Integer.valueOf(text);
            } catch (NumberFormatException ignore) {
                return null;
            }
        }
    }

    public static class LongDeserializer extends StdDeserializer<Long> {

        private static final long serialVersionUID = 1L;

        public LongDeserializer() {
            super(Long.class);
        }

        @Override
        public Long deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            String text = jp.getText();
            if (null == text || text.isEmpty()) {
                return null;
            }
            try {
                return Long.valueOf(text);
            } catch (NumberFormatException ignore) {
                return null;
            }
        }
    }

    public static class LongSerializer extends StdSerializer<Long> {

        private static final long serialVersionUID = 1L;

        public LongSerializer() {
            super(Long.class);
        }

        @Override
        public void serialize(Long value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            // 序列化long型数值时，为了防止在web端丢失精度，如果数值大于js的Number.MAX_SAFE_INTEGER或小于Number.MIN_SAFE_INTEGER，把数值序列化成字符串
            if (value > 0x1fffffffffffffL || value < -0x1fffffffffffffL) {
                jgen.writeString(Long.toString(value));
            } else {
                jgen.writeNumber(value);
            }
        }
    }
}
